package com.chalk.common.util;

import com.chalk.common.constant.CommonConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author xiaoqiang
 * @Description Redis 工具类
 * @date 2018/12/17 11:01
 */
@Component
public class RedisUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(RedisUtil.class);

    @Autowired
    private RedisTemplate<Serializable, Object> redisTemplate;

    /**
     * 加锁
     * @param key   业务key
     * @param value 当前时间+超时时间
     * @return
     */
    public boolean lock(String key, Long value) {
        // 使用 redis 的 SETNX
        if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
            return true;
        }
        /* 避免死锁,且只让一个线程拿到锁 */
        Long currentValue =  (Long) redisTemplate.opsForValue().get(key);
        /* 如果锁过期了 */
        if (currentValue != null && currentValue.longValue() < System.currentTimeMillis()) {
            /* 获取上一个锁的时间 */
            Long oldValue = (Long) redisTemplate.opsForValue().getAndSet(key, value);
            /* 只会让一个线程拿到锁，如果旧的value和currentValue相等，只会有一个线程达成条件，因为第二个线程拿到的oldValue已经和currentValue不一样了 */
            if (oldValue != null && oldValue.longValue() == currentValue.longValue()) {
                return true;
            }
        }
        return false;
    }


    /**
     * 解锁
     * @param key
     * @param value
     */
    public void unlock(String key, Long value) {
        try {
            Long currentValue = (Long) redisTemplate.opsForValue().get(key);
            if (currentValue != null && value != null && currentValue.longValue() == value.longValue()) {
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        } catch (Exception e) {
            LOGGER.error("『redis分布式锁』解锁异常，{}", e);
        }
    }

    /**
     * 将 key，value 存放到redis数据库中
     *
     * @param key
     * @param value
     */
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 将 key，value 存放到redis数据库中，设置过期时间单位是秒
     *
     * @param key
     * @param value
     * @param expireTime
     */
    public void set(String key, Object value, long expireTime) {
        redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 判断 key 是否在 redis 数据库中
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 获取与 key 对应的对象
     * @param key
     * @param clazz 目标对象类型
     * @return T
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key, Class<T> clazz) {
        Object obj = redisTemplate.opsForValue().get(key);
        if (obj == null) {
            return null;
        }
        return (T) obj;
    }

    /**
     * 删除 key 对应的 value
     * @param key
     */
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 返回指定key下的元素个数
     * @param key
     * @return
     */
    public int keys(String key){
        Set<Serializable> keys = redisTemplate.keys(key);
        return keys.size();
    }

    /**
     * 返回过期时间
     * @param key
     * @return
     */
    public long getExpire(String key){
        return redisTemplate.getExpire(key);
    }

    /**
     * 存储List
     * @param key
     * @param value
     * @param expireTime
     */
    public void setList(String key, Object value, long expireTime){
        redisTemplate.expire(key, expireTime, TimeUnit.DAYS);
        //value相同则删除
        redisTemplate.opsForList().remove(key, 0, value);
        redisTemplate.opsForList().leftPush(key, value);
        // 保留指定长度得list
        this.reserved(key, 0, CommonConstants.FOOT_PRINGT_MAX_SIZE);
    }

    /**
     * 获取List
     * @param key
     * @return
     */
    public List<Object> getList(String key,Integer current, Integer size) {
        // 返回存储在键中的列表的指定元素。偏移开始和停止是基于零的索引，其中0是列表的第一个元素（列表的头部），1是下一个元素
        List<Object> list = redisTemplate.opsForList().range(key,current,size);
        return list;
    }

    /**
     * 保留指定长度得list
     * @param key
     * @param start
     * @param end
     */
    public void reserved(String key, long start, long end){
        redisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * 删除list中指定的元素
     * @param key
     * @param index
     */
    public void trim(String key, Long index){
        redisTemplate.opsForList().trim(key, index, -1);
    }

    /**
     * 将list中指定的索引修改长value
     * @param key
     * @param index
     * @param value
     */
    public void setIndex(String key, Long index,Object value){
        redisTemplate.opsForList().set(key,index,value);
    }

    /**
     * 删除value相同得数据
     * @param key
     * @param value
     */
    public void delList(String key,Object value){
        redisTemplate.opsForList().remove(key, 0, value);
    }


    /*redisTemplate hashMap*/

    /**
     *添加 hashmap数据
     * @param key
     * @param value
     */
    public void addHashMap(String key,String Hkey,Object value){
        redisTemplate.opsForHash().put(key,Hkey,value);
    }

    /**
     * 获取key所有的值
     * @param key
     */
    public List<Object> getHashMap(String key){
       return redisTemplate.opsForHash().values(key);
    }

    /**
     * 删除key中的键值对
     * @param key
     * @param objects
     */
    public void delHashMap(String key,Object ...objects){
        redisTemplate.opsForHash().delete(key,objects);
    }

    /**
     * 判断变量中是否有指定的map键
     * @param key
     */
    public boolean boolHashMap(String key,String Hkey){
        return redisTemplate.opsForHash().hasKey(key,Hkey);
    }

    /**
     * 给集追加数据
     * @param key redis key
     * @param value 添加的值
     * @return
     */
    public Long sAdd(String key,Object value){
        return redisTemplate.opsForSet().add(key, value);
    }

    /**
     * 移除集合中的元素
     * @param key redis key
     * @param value 添加的值
     * @return
     */
    public Long remove(String key,Object value){
        return redisTemplate.opsForSet().remove(key, value);
    }

    /**
     * 判断 member 元素是否是集合 key 的成员
     * @param key redis key
     * @param member 元素
     * @return
     */
    public boolean isMember(String key,Object member){
        return redisTemplate.opsForSet().isMember(key, member);
    }

}
